/******************************************************************************************************
Copyright (C) 2017 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals qsTr, Application, Dir, File, MessageBox, MnUi, SysInfo, TextStream, Process*/

/*jslint plusplus: true, continue: true, indent: 4*/

function dbSearchMSInNIST() {
	"use strict";

	var molecule, dbNISTSearchResult, dbNISTSearchPathApplication, commandArguments, molFileContent;


	//---------------------Instance attributes to store the settings---------------------

	//It defines the environment variable which contains the ini file
	dbSearchMSInNIST.prototype.environmentVarName = "windir";

	//It sets a list of possible directories which could store the ini file. This var is an alternative solution if the var name does not exist
	dbSearchMSInNIST.prototype.possiblePathsForIniFile = ["C:/Windows", "C:/WINNT"];

	//It defines the name of the ini file which contains the var with the binary path within the expected section
	dbSearchMSInNIST.prototype.iniFileName = "win.ini";

	//It configures the name of the section within the ini file which contains the var of the binary file path
	dbSearchMSInNIST.prototype.winIniExpectedSection = "[NISTMS]";

	//Instance attribute which allows to retrieve the MS Search Program’s folder (location of the executable files). It should work both with 32 and 64 bits versions (ie: Path=, Path32=, Path64=)
	/*jslint regexp: true*/
	dbSearchMSInNIST.prototype.applicationPathRegex = /^Path\d*\s*=\s*.*$/mi;
	/*jslint regexp: false*/

	//It set the filename of the executable binary file
	dbSearchMSInNIST.prototype.executableFilename = "NISTMS$.EXE";

	//It stores the temp molfile with the content of the active molecule
	dbSearchMSInNIST.prototype.tempMolfile = Dir.temp() + "/" + "NISTSearchIntegrationWithMnova.mol";

	//---------------------Main Program---------------------

	//This search feature only works in computer with Windows operating systems installed
	if (SysInfo.isWin) {

		dbNISTSearchResult = dbSearchMSInNIST.checkApplicationStatus(dbSearchMSInNIST.prototype.environmentVarName, dbSearchMSInNIST.prototype.possiblePathsForIniFile, dbSearchMSInNIST.prototype.iniFileName, dbSearchMSInNIST.prototype.winIniExpectedSection, dbSearchMSInNIST.prototype.applicationPathRegex);
		if (!(dbNISTSearchResult.isInstalled)) {
			MessageBox.critical(qsTr("NIST MS Search program not detected on this computer"));
		} else {

			//Get the base folder of the NIST Search Program
			dbNISTSearchPathApplication = dbNISTSearchResult.path;
			dbNISTSearchPathApplication += "/" + dbSearchMSInNIST.prototype.executableFilename;

			//Check for the executable binary file
			if (!File.exists(dbNISTSearchPathApplication)) {
				MessageBox.critical(qsTr("NIST MS Search program not detected on this computer"));
			} else {

				//Get molecule to perform the search against NIST database
				molecule = Application.molecule.activeMolecule();
				if (molecule === undefined) {
					MessageBox.critical(qsTr("A molecule is needed to perform the search"));
				} else if (!molecule.isValid()) {
					MessageBox.critical(qsTr("The active molecule is not valid"));
				} else {

					//Create a temp file with the active molfile content
					molFileContent = molecule.getMolfile();
					if (dbSearchMSInNIST.writeToFile(dbSearchMSInNIST.prototype.tempMolfile, molFileContent)) {

						//Prepare the arguments of the NIST command
						commandArguments = [];
						commandArguments.push("/MOL=" + dbSearchMSInNIST.prototype.tempMolfile.replace(/\//g, "\\"));

						//Launch the NIST Search Application
						dbSearchMSInNIST.prototype.dbNISTApplicationProcess = new Process();
						dbSearchMSInNIST.prototype.dbNISTApplicationProcess.start(dbNISTSearchPathApplication, commandArguments);

						//It checks if the program was launched successfully
						if (!dbSearchMSInNIST.prototype.dbNISTApplicationProcess.waitForStarted()) {
							MessageBox.critical(qsTr("Failed to start the NIST MS Search Program"));
						}
					} else {
						MessageBox.critical(qsTr("A problem has occurred writing the temp molfile at:\n{0}").format(dbSearchMSInNIST.prototype.tempMolfile));
					}
				}
			}
		}
	} else if (SysInfo.isLinux) {
		MessageBox.critical(qsTr("It has been detected a GNU/Linux operating system but this feature only works with Microsoft Windows"));
	} else if (SysInfo.isMac) {
		MessageBox.critical(qsTr("It has been detected a Macintosh operating system but this feature only works with Microsoft Windows"));
	} else {
		MessageBox.critical(qsTr("The NIST MS Search tool only works in Microsoft Windows operating systems"));
	}
}

//---------------------Static Methods---------------------

dbSearchMSInNIST.readFile = function (aPath, aNumLines) {
	"use strict";

	var stream, file, string, totalLines, auxString;

	file = new File(aPath);
	file.open(File.ReadOnly);
	stream = new TextStream(file);
	if (aNumLines !== undefined && aNumLines > 0) {
		string = "";
		totalLines = 0;
		while (!stream.atEnd() && totalLines < aNumLines) {
			string += stream.readLine() + "\n";
			totalLines++;
		}
		if (totalLines === 1 && string.length > 2) {
			string = string.slice(0, -1);
			auxString = string.split(/\r?\n|\n|\r/g, aNumLines - 1);
			string = auxString.join("\n");
		}
		stream = string;
	} else {
		stream = stream.readAll();
	}

	file.close();

	return stream;
};

dbSearchMSInNIST.writeToFile = function (aFilePath, aData) {
	"use strict";

	var file, resultFile, result;

	//Write string into a file
	result = false;
	file = new File(aFilePath);
	if (File.exists(aFilePath)) {
		File.remove(aFilePath);
	}
	file.open(File.ReadWrite);
	resultFile = new TextStream(file);
	resultFile.codec = "UTF-8";
	resultFile.write(aData);
	resultFile.flush();
	file.close();
	if (File.exists(aFilePath)) {
		result = true;
	}

	return result;
};

dbSearchMSInNIST.checkApplicationStatus = function (aEnvironmentVarName, aPossiblePathsForIniFile, aIniFileName, aIniSectionExpected, aApplicationPathRegex) {
	"use strict";

	var i, j, result, winDir, winDirPath, envVarName, envVars, flagWinDir, auxDir,
		winIniFilePaths, flagAlternativePaths, winIniFilePath, winIniFileContent,
		winIniExpectedSection, winIniFilePos, winIniSection, applicationPathRegex,
		applicationPathString, resultRegex, applicationPathArray, auxPathApplication;

	//Initialize local vars
	result = {};
	result.isInstalled = false;
	winIniExpectedSection = aIniSectionExpected;
	applicationPathRegex = aApplicationPathRegex;
	winDir = undefined;
	flagAlternativePaths = false;

	//Location of the NIST MS Search Program
	//It is necessary to determine the location of the NIST MS Search Program file and the location of the NIST MS Search working directory.
	//These may be found in the section of the INI file located in the %windir% directory.
	envVars = Process.systemEnvironmentObject();
	flagWinDir = false;
	for (envVarName in envVars) {
		if (envVars.hasOwnProperty(envVarName)) {
			if (envVarName.toString().toLowerCase() === aEnvironmentVarName) {
				winDirPath = envVars[envVarName];
				winDir = new Dir(winDirPath);
				if (winDir.exists) {
					flagWinDir = true;
					break;
				} else {
					winDir = undefined;
					winDirPath = undefined;
				}
			}
		}
	}

	//Now we look for the most common directories if we didn't find the right env var.
	if (aPossiblePathsForIniFile !== undefined) {
		for (i = 0; ((flagWinDir && !flagAlternativePaths) || (i < aPossiblePathsForIniFile.length)); i++) {

			//This prior check allows to know if the windir has been found by using the environment vars
			if (winDir === undefined) {
				if (!flagWinDir && !flagAlternativePaths) {
					flagAlternativePaths = true;
					i = -1;
					continue;
				}

				//Get the directory object from the possible path list
				winDirPath = aPossiblePathsForIniFile[i];
				winDir = new Dir(winDirPath);
			}

			//Looking for the INI file
			if (winDir !== undefined && winDir.exists) {
				winIniFilePaths = winDir.entryListAdv(aIniFileName,  Dir.IgnoreCase, Dir.IgnoreCase);

				//It searchs into each file
				for (j = 0; j < winIniFilePaths.length && !result.isInstalled; j++) {
					winIniFilePath = winIniFilePaths[j];
					winIniFileContent = dbSearchMSInNIST.readFile(winIniFilePath);

					//Looking for the right section of the ini file
					winIniFilePos = winIniFileContent.indexOf(winIniExpectedSection);
					if (winIniFilePos > -1) {
						winIniSection = winIniFileContent.substring(winIniFilePos, winIniFileContent.length);
						resultRegex = winIniSection.match(applicationPathRegex);

						//Get the left part just before the equal symbol which has the value of the PATH
						if (resultRegex !== null && resultRegex.length > 0) {
							applicationPathString = resultRegex[0];
							applicationPathArray = applicationPathString.split("=");
							if (applicationPathArray.length > 1) {

								//Remove both possible blank spaces and quotes
								auxPathApplication = applicationPathArray[1].toString().trim();
								applicationPathString = auxPathApplication.replace(/"/g, "");
								auxPathApplication = applicationPathString;
								applicationPathString = auxPathApplication.replace(/'/g, "");

								//Despite finding the directory, it only will be considered as installed if it exists
								if (applicationPathString !== undefined && applicationPathString !== "") {
									auxDir = new Dir(applicationPathString);
									if (auxDir.exists) {
										result.isInstalled = true;
										result.path = auxDir.absPath;
										return result;
									}
								}
							}
						}
					}
				}
				if (!result.isInstalled) {
					winDir = undefined;
					if (i === 0 && flagWinDir) {
						flagWinDir = false;
					}
				}
			} else {
				winDir = undefined;
			}
		}
	}

	return result;
};

if (this.MnUi && this.MnUi.scripts_common) {
	MnUi.scripts_common.scripts_common_SearchMSinNIST = dbSearchMSInNIST;
}
